#include "triangle_iterator.h"
#include <boost\numeric\interval.hpp>
#include <gmpxx.h>

triangle_iterator::triangle_iterator(const segment& _segm12, const segment& _segm23, const segment& _segm13, bool _finished)
{
	segm12 = _segm12;
	segm13 = _segm13;
	segm23 = _segm23;

	finished = _finished;
}

triangle_iterator::triangle_iterator(const segment& _segm12, const segment& _segm23, const segment& _segm13)
{
	segm12 = _segm12;
	segm13 = _segm13;
	segm23 = _segm23;

	min_x = std::min(floor(segm12.first.x), std::min(floor(segm23.first.x), floor(segm13.second.x)));
	max_x = std::max(floor(segm12.first.x), std::max(floor(segm23.first.x), floor(segm13.second.x)));

	next_min_y = std::numeric_limits<double>::max(), next_max_y = -std::numeric_limits<double>::max();
	min_y = std::numeric_limits<double>::max(), max_y = -std::numeric_limits<double>::max();

	crossed = false, crossed_next = false, finished = false;

	first_check(segm12);
	first_check(segm23);
	first_check(segm13);

	y = floor(std::min(min_y - crossed, next_min_y));
	x = min_x;

	bound = std::max(max_y, next_max_y) * (1 - std::numeric_limits<double>::epsilon());
}

void triangle_iterator::count_new_column()
{
	min_y = next_min_y;
	max_y = next_max_y;
	crossed = crossed_next;

	next_min_y = std::numeric_limits<double>::max(), next_max_y = -std::numeric_limits<double>::max();

	check_minmax_y(segm12, x + 1);
	check_minmax_y(segm23, x + 1);
	check_minmax_y(segm13, x + 1);

	bound = std::max(max_y, next_max_y) * (1 - std::numeric_limits<double>::epsilon());

	y = floor(std::min(min_y - crossed, next_min_y));
}

triangle_iterator& triangle_iterator::operator++()
{
	if (x == min_x)
	{
		y++;
		if (y <= bound)
			return *this;
		else
		{
			x++;
			new_column = true;
		}
	}
	if (new_column)
		count_new_column();
	if (x < max_x)
	{
		if (new_column)
			new_column = false;
		else
			y++;

		if (y > bound)
		{
			x++;
			count_new_column();
		}
		return *this;
	}
	else
	{
		y++;
		if (y >= next_max_y)
			finished = true;
		return *this;
	}
}

bool triangle_iterator::operator !=(const triangle_iterator& p) const
{
	return (!(*this == p));
}

bool triangle_iterator::operator ==(const triangle_iterator& p) const
{
	return ((segm12 == p.segm12) && (segm13 == p.segm13) && (segm23 == p.segm23) && (finished == p.finished));
}

double triangle_iterator::y_at_wrapper(const segment& segm, int x) const
{
	boost::numeric::interval<double> tmp1;

	segm.y_at(x, tmp1);

	if (floor(tmp1.lower()) == floor(tmp1.upper()))
		return (tmp1.lower() + tmp1.upper()) / 2;
	else
	{
		mpq_class tmp2;

		segm.y_at(x, tmp2);

		mpq_class floored_tmp2(floor(tmp2));

		if (tmp2 == floored_tmp2)
			return floored_tmp2.get_d();
		else
			return floored_tmp2.get_d() + sgn(tmp2 - floored_tmp2) * 0.5;
	}
}

void triangle_iterator::check_minmax_y(const segment& segm, int x)
{
	if (segm.intersects_with_x(x))
	{
		double tmp = y_at_wrapper(segm, x);

		if (tmp == std::numeric_limits<double>::infinity())
		{
			if (next_min_y < std::min(segm.first.y, segm.second.y))
			{
				next_min_y = std::min(segm.first.y, segm.second.y);
				crossed_next = (segm.first.x == segm.second.x);
			}
			next_max_y = std::max(next_max_y, std::max(segm.first.y, segm.second.y));
		} 
		else
		{
			if ((tmp == static_cast<int>(tmp)) && (segm.slope < 0))
			{
				if (tmp - 1 < next_min_y)
				{
					next_min_y = tmp;
					crossed_next = true;
				}
			}
			else if (tmp < next_min_y)
			{
				next_min_y = tmp;
				crossed_next = false;
			}
			next_max_y = std::max(next_max_y, tmp);
		}
	}
};

void triangle_iterator::first_check(const segment& segm)
{
	if (segm.intersects_with_x(min_x))
	{
		double tmp = y_at_wrapper(segm, x);

		if (tmp == std::numeric_limits<double>::infinity())
		{
			if (min_y < std::min(segm.first.y, segm.second.y))
			{
				min_y = std::min(segm.first.y, segm.second.y);
				crossed = (segm.first.x == segm.second.x);
			}

			max_y = std::max(max_y, std::max(segm.first.y, segm.second.y));
		} 
		else
		{
			if ((tmp == static_cast<int>(tmp)) && (segm.slope < 0))
			{
				if (tmp - 1 < min_y)
				{
					min_y = tmp;
					crossed = true;
				}
			}
			else if (tmp < min_y)
			{
				min_y = tmp;
				crossed = false;
			}
			max_y = std::max(max_y, tmp);
		}
	}

	check_minmax_y(segm, min_x + 1);
};